package advice.org.springframework.util;

import advice.org.springframework.asm.ClassReaderAdvice;
import com.dcits.agent.IAdvice;
import com.dcits.agent.loader.ModifyClassLoader;
import com.dcits.agent.loader.SmartClassLoader;
import com.dcits.agent.utils.RedefineClassUtils;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.ModifierReviewable;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import org.kohsuke.MetaInfServices;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.io.InputStream;

/**
 * @className: ClassUtilsAdvice
 * @description:
 * @author: caoyangjie
 * @date: 2025/6/21
 **/
@MetaInfServices
public class ClassUtilsAdvice implements IAdvice {

    @Advice.OnMethodExit
    static void onMethodExit(@Advice.Return(readOnly = false) Class clazz,
                             @Advice.Argument(value = 0) String className
    ) {
        if( includeClass(className) ) {
            clazz = replaceClass(className);
        }
    }

    public static Class replaceClass(String className) {
        SmartClassLoader smartClassLoader = SmartClassLoader.getInstance();
        if( smartClassLoader.findClass(className) != null ) {
            return smartClassLoader.findClass(className);
        }
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(className.replaceAll("\\.", "\\/") + ".class");
        byte[] classBytes = new byte[0];
        try {
            classBytes = inputStream.readAllBytes();
            byte[] modifiedClassBytes = ClassReaderAdvice.modifyClassFile(classBytes);
            return smartClassLoader.defineClassFromBytes(className, modifiedClassBytes);
        } catch (Throwable e) {
        } finally {
            ClassReaderAdvice.loaderIncludeClasses();
        }
        return SmartClassLoader.getInstance().findClass(className);
    }

    public static boolean includeClass(String name) {
        String[] classNames = new String[]{
                "cn.com.zybank.**.InitConfigRunner",
                "cn.com.zybank.**.APIMonitorFilter",
                "cn.com.zybank.**.APIMonitorHandleRunnable",
                "cn.com.zybank.**.APIMonitorService",
                "cn.com.zybank.**.AuthController",
                "cn.com.zybank.**.BaseController",
                "cn.com.zybank.**.ClientUtil",
                "cn.com.zybank.**.CorsFilter",
                "cn.com.zybank.**.DcitsFeignClient",
                "cn.com.zybank.**.DcitsRule",
                "cn.com.zybank.**.EMSPBlockExceptionHandler",
                "cn.com.zybank.**.EMSPOriginParser",
                "cn.com.zybank.**.EMSPResourceAspect",
                "cn.com.zybank.**.EMSPWebInterceptor",
                "cn.com.zybank.**.EntityUtils",
                "cn.com.zybank.**.FilterConfig",
                "cn.com.zybank.**.FilterConfiguration",
                "cn.com.zybank.**.FormatCompent",
                "cn.com.zybank.**.GlobalExceptionHandler",
                "cn.com.zybank.**.InterfaceAvailabilityFilter",
                "cn.com.zybank.**.IPAuthRestFilter",
                "cn.com.zybank.**.IPAuthRestInterceptor",
                "cn.com.zybank.**.ResponseWrapper",
                "cn.com.zybank.**.SeataRequestFilter",
                "cn.com.zybank.**.ServiceAuthConfig",
                "cn.com.zybank.**.ServiceAuthRestFilter",
                "cn.com.zybank.**.ServiceAuthRestInterceptor",
                "cn.com.zybank.**.ServiceInfoFilter",
                "cn.com.zybank.**.SmartMultipleDataSourceAnnotationInterceptor",
                "cn.com.zybank.**.TokenAuthRestFilter",
                "cn.com.zybank.**.TokenAuthRestInterceptor",
                "cn.com.zybank.**.TraceEntryRecordFilter",
                "cn.com.zybank.**.TransactionMonitorInterceptor",
                "cn.com.zybank.**.TransactionMonitorService",
                "cn.com.zybank.**.WebhookController",
                "cn.com.zybank.**.ZYBankHeadUtil",
        };
        for (String className : classNames) {
            if( className.contains("*") && !className.endsWith("*") ) {
                if( name.startsWith(className.replace("*", "")) ) {
                    return true;
                }
            }
            if( className.contains("**") && !className.endsWith("*") ) {
                // className 使用 ** 分隔后，判断 name startsWith ** 分隔前面的内容， name endsWith ** 分隔后面的内容
                String[] split = className.split("\\*\\*");
                if( name.startsWith(split[0]) && name.endsWith(split[1]) ) {
                    return true;
                }
            }
            if (name.equalsIgnoreCase(className)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void advice() {
        // 这里需要根据实际情况填写 redefine 方法
        ElementMatcher<? super MethodDescription> matcher = ElementMatchers
                // 只匹配 TARGET_METHODS 中的方法
                .namedOneOf("forName")
                 // 确保匹配的是静态方法
                .and(ElementMatchers.isStatic())
                .and(ModifierReviewable.OfByteCodeElement::isPublic);

        // 增强当前类中所有的非构造函数的 public 方法
        RedefineClassUtils.redefineMethodWithAdvice(ClassUtils.class, matcher);
    }
}
